package libsvm;

import java.rmi.*;

public class DistMultiSvm {

  /*
  static public String[] m_vecServers = {"128.83.130.220",
					 "128.83.130.221",
					 "128.83.130.222",
					 "128.83.130.223",
					 "128.83.130.224",
					 "128.83.130.225",
					 "128.83.130.226",
					 "128.83.130.227",
					 "128.83.130.240",
					 "128.83.130.241",
					 "128.83.120.116"};
  */
  static public String[] m_vecServers = {"128.83.144.118"};

  svm_parameter m_param;
  int m_nr_class;
  int[] m_start;
  int[] m_count;
  svm_node[][] m_x;
  svm.decision_function[] m_f;
  double[] m_probA;
  double[] m_probB;
  double[] m_weighted_C;
  boolean[] m_nonzero;

  int m_i;
  int m_j;
  int m_p;

  public DistMultiSvm()
  {
    m_i = 0;
    m_j = m_i + 1;
    m_p = 0;
  }


  public void train()
  {
    DistOneSvm[] slaves = new DistOneSvm[m_vecServers.length];

    try {
      for (int i = 0; i < m_vecServers.length; i++) {
	slaves[i] = new DistOneSvm(this, m_vecServers[i]);
	slaves[i].start();
      }
      
      for (int i = 0; i < m_vecServers.length; i++)
	slaves[i].join();
    }
    catch (InterruptedException e) {
      e.printStackTrace();
    }
  }


  public synchronized svm_problem nextProblem(int[] pos)
  {
    if (m_i == m_nr_class)
      return null;

    svm_problem sub_prob = new svm_problem();
    int si = m_start[m_i], sj = m_start[m_j];
    int ci = m_count[m_i], cj = m_count[m_j];
    sub_prob.l = ci + cj;
    sub_prob.x = new svm_node[sub_prob.l][];
    sub_prob.y = new double[sub_prob.l];
    int k;
    for (k = 0; k < ci; k++) {
      sub_prob.x[k] = m_x[si+k];
      sub_prob.y[k] = +1;
    } 
    for (k = 0; k < cj; k++) {
      sub_prob.x[ci+k] = m_x[sj+k];
      sub_prob.y[ci+k] = -1;
    }

    pos[0] = m_i;
    pos[1] = m_j;
    pos[2] = m_p;
 
    m_j++;
    if (m_j == m_nr_class) {
      m_i++;
      m_j = m_i + 1;
      if (m_j == m_nr_class)
	m_i++;
    }
    m_p++;

    return sub_prob;
  }


  public synchronized void setResults(svm.decision_function df, int[] pos)
  {
    int i = pos[0];
    int j = pos[1];
    int p = pos[2];

    m_f[p] = df;

    int si = m_start[i], sj = m_start[j];
    int ci = m_count[i], cj = m_count[j];

    int k;
    for (k = 0; k < ci; k++)
      if(!m_nonzero[si+k] && Math.abs(m_f[p].alpha[k]) > 0)
	m_nonzero[si+k] = true;
    for (k = 0; k < cj; k++)
      if(!m_nonzero[sj+k] && Math.abs(m_f[p].alpha[ci+k]) > 0)
	m_nonzero[sj+k] = true;  
  }
}


class DistOneSvm extends Thread {

  DistMultiSvm m_msvm;
  String m_strServer;

  public DistOneSvm(DistMultiSvm msvm, String strServer)
  {
    m_msvm = msvm;
    m_strServer = strServer;
  }


  public void run()
  {
    int[] pos = new int[3];
    svm_problem prob = m_msvm.nextProblem(pos);
    while (prob != null) {
      // set a security manager
      System.setSecurityManager(new RMISecurityManager());
      
      try {
	String location = "rmi://" + m_strServer + "/RemoteSvmImplementation";
	
	RemoteSvmInterface rsi = (RemoteSvmInterface) Naming.lookup(location);
	System.out.println("Got a remote reference to " + location + "!");
	System.out.println("Making remote call to the server " + location +
			   " for decision " + pos[2] + "!");
	
	svm.decision_function df = 
	  rsi.svm_train_one(prob, m_msvm.m_param,
			    m_msvm.m_weighted_C[pos[0]],
			    m_msvm.m_weighted_C[pos[1]]);

	System.out.println("Returned from remote call to " + location + ", " +
			   pos[0] + " vs. " + pos[1] + "!");

	m_msvm.setResults(df, pos);

	// get next problem
	prob = m_msvm.nextProblem(pos);	
      } 
      catch (Exception e) {
	e.printStackTrace();
      }
    }
  }


}
